﻿// -----------------------------------------------------------------------
//  <copyright file="OnlineUserProvider.cs" company="LiuliuSoft">
//      Copyright (c) 2025 66SOFT. All rights reserved.
//  </copyright>
//  <site>https://ifs.66soft.net</site>
//  <last-editor>郭明锋</last-editor>
//  <last-date>2025-09-27 15:09</last-date>
// -----------------------------------------------------------------------

using Microsoft.Extensions.Caching.Distributed;
using OSharp.Threading.Asyncs;
using OSharp.Caching;


namespace DaprPlus.Identity.Application;

public class OnlineUserProvider<TUser, TUserClaim, TUserLogin, TUserToken, TRole, TUserRole>(IServiceProvider provider)
    : IOnlineUserProvider
    where TUser : UserPlusBase<TUserClaim, TUserLogin, TUserRole, TUserToken>, new()
    where TUserClaim : UserClaimBase, new()
    where TUserLogin : UserLoginBase, new()
    where TUserRole : UserRoleBase, new()
    where TUserToken : UserTokenBase, new()
    where TRole : RoleBase, new()
{
    private readonly IDistributedCache _cache = provider.GetRequiredService<IDistributedCache>();
    private readonly AsyncLock _asyncLock = new();

    public async Task<OnlineUser?> GetAsync()
    {
        var userName = GetCurrentUserName();
        if (userName == null)
        {
            return null;
        }

        var key = GetKey(userName);
        var options = new DistributedCacheEntryOptions();
        options.SetSlidingExpiration(TimeSpan.FromMinutes(10));
        using (await _asyncLock.LockAsync())
        {
            return await _cache.GetAsync(key, () =>
            {
                return provider.ExecuteScopedWorkAsync(async scopedProvider =>
                {
                    var userManager = scopedProvider.GetRequiredService<UserManager<TUser>>();
                    var user = await userManager.FindByNameAsync(userName);
                    if (user == null)
                    {
                        return null;
                    }
                    var roles = await userManager.GetRolesAsync(user);
                    var roleManager = scopedProvider.GetRequiredService<RoleManager<TRole>>();
                    var isAdmin = roleManager.Roles.Any(m => roles.Contains(m.Name) && m.IsAdmin);
                    var onlineUser = new OnlineUser()
                    {
                        Id = user.Id,
                        UserName = user.UserName,
                        NickName = user.NickName,
                        Email = user.Email,
                        Avatar = user.Avatar,
                        IsAdmin = isAdmin,
                        SecurityStamp = user.SecurityStamp,
                        Roles = roles.ToArray()
                    };
                    var claims = await userManager.GetClaimsAsync(user);
                    foreach (var claim in claims)
                    {
                        onlineUser.ExtendData.Add(claim.Type, claim.Value);
                    }

                    return onlineUser;

                });
            }, options);
        }
    }

    public async Task RemoveAsync(string? userName = null)
    {
        userName ??= GetCurrentUserName();
        if (string.IsNullOrEmpty(userName))
        {
            return;
        }
        var key = GetKey(userName);
        await _cache.RemoveAsync(key);
    }

    private static string GetKey(string userName)
    {
        return $"Share:OnlineUser:{userName}";
    }

    private string? GetCurrentUserName()
    {
        var principal = provider.GetCurrentUser();
        if (principal is not { Identity.IsAuthenticated: true })
        {
            return null;
        }
        var userName = principal.Identity.Name!;
        return userName;
    }
}